//
//  ChartsContentsVC.swift
//  TimeBoat
//
//  Created by bo wang on 2022/8/23.
//  Copyright © 2022 Time Boat. All rights reserved.
//

import UIKit
import Charts

class ChartsContentsVC: AppBaseVC {
    
    /// 图表的内容主题。 在创建控制器后必须设置
    var subject: ChartsSubject!
    /// 粒度。在创建控制器后必须设置
    var granularitie: DateGranularity!
    /// 为了显示磨砂效果，UIScrollView是全屏大小的，在这里设置inset避免被遮挡
    var contentInsetTop: CGFloat = 0
    
    /// 当前图表的日期
    var date: Date = Date()
    
    /// 子类需要将3个按钮连接这里的属性
    @IBOutlet var dateBtn: UIButton?
    @IBOutlet var previousDateBtn: UIButton?
    @IBOutlet var nextDateBtn: UIButton?
    @IBOutlet var summaryInfoView: UIView?
    
    // TODO: IMEI需要从设备获取
    var imei: String? { AccountManager.shareAccount().deviceImei() }
    
    /// 子类重载的属性，返回粒度下图表的类型
    var chartViewType: ChartViewType { .line }
    
    /// 子类需要将UIScrollView连接到此属性
    @IBOutlet var scrollView: UIScrollView?
    /// 子类需要将图表视图容器连接到此属性
    @IBOutlet var chartViewContainer: ChartAdapterView!
    
    /// line 图表，如果有的话
    var lineChartView: LineChartView? { chartViewContainer.line }
    /// bar 图表，如果有的话
    var barChartView: BarChartView? { chartViewContainer.bar }
    /// 图表，如果还没创建，返回nil
    var chartView: BarLineChartViewBase? { chartViewContainer.cur }
    
    private(set) var curData: ChartData?

    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView?.contentInset.top = contentInsetTop
        loadChartView()
        let dt = Date().addingTimeInterval(TimeInterval(TimeZone.current.secondsFromGMT()))
        update(date: dt)
        summaryInfoView?.layer.masksToBounds = true
        summaryInfoView?.layer.cornerRadius = 16
    }
    
    /// 子类可以重载此属性返回需要的Y轴数据粒度，为nil时不设置粒度
    var yValGranularitie: Double? { 1 }
    /// 子类可以重载此属性返回需要的X轴数据粒度，为nil时不设置粒度
    var xValGranularitie: Double? { 1 }
    /// 子类可以重载此属性返回需要的X轴范围，为nil时不设置范围
    var xValRange: ClosedRange<Double>? { 0...1 }

    /// 加载图标view，需要子类根据粒度自行决定图表的配置
    func loadChartView() {
        if self.chartViewContainer == nil{return}
        chartViewContainer.using(chart: chartViewType)
        chartView?.leftAxis.valueFormatter = self
        if let yValGranularitie = yValGranularitie {
            chartView?.leftAxis.granularity = yValGranularitie
            chartView?.leftAxis.granularityEnabled = true
        }
        if let range = xValRange {
            chartView?.xAxis.axisMinimum = range.lowerBound
            chartView?.xAxis.axisMaximum = range.upperBound
        }
        if let xValGranularitie = xValGranularitie {
            chartView?.xAxis.granularity = xValGranularitie
            chartView?.xAxis.granularityEnabled = true
        }
        chartView?.xAxis.valueFormatter = self
    }
    
    /// 加载数据。根据粒度调用各自的方法
    func loadData() {
        switch granularitie {
        case .day: loadDayData(date)
        case .week: loadWeekData(date)
        case .month: loadMonthData(date)
        case .year: loadYearData(date)
        case .none: break
        }
    }
    
    /// 子类重载此方法加载日数据
    func loadDayData(_ date: Date) {}
    /// 子类重载此方法加载周数据
    func loadWeekData(_ date: Date) {}
    /// 子类重载此方法加载月数据
    func loadMonthData(_ date: Date) {}
    /// 子类重载此方法加载年数据
    func loadYearData(_ date: Date) {}

    /// 请求到数据后使用此方法刷新图表
    func reloadChartView(data: ChartData?, animated: Bool = true) {
        self.curData = data
        chartView?.data = data
        chartView?.animate(xAxisDuration: 0.25)
    }
    
    /// 请求数据失败时调用
    func onLoadDataFailed(_ errDesc: String) {
        reloadChartView(data: nil)
        HUD.showFail(msg: errDesc)
    }
    
    /// 时间发生了更改
    func update(date: Date) {
        self.date = date
        loadData()
        
        dateBtn?.setTitle(dateTitle(of: date, granularity: granularitie), for: .normal)
        
        let previous = previousDate(of: date, granularity: granularitie)
        let next = nextDate(of: date, granularity: granularitie)
        previousDateBtn?.isHidden = previous == nil
        nextDateBtn?.isHidden = next == nil
    }
    
    /// 子类需要将各自的UIButton连线到此方法
    @IBAction func onTapPreviousDate(_ btn: UIButton) {
        guard let newDate = previousDate(of: date, granularity: granularitie) else { return }
        update(date: newDate)
    }
    
    /// 子类需要将各自的UIButton连线到此方法
    @IBAction func onTapNextDate(_ btn: UIButton) {
        guard let newDate = nextDate(of: date, granularity: granularitie) else { return }
        update(date: newDate)
    }
    
    /// 子类需要将各自的UIButton连线到此方法
    @IBAction func onTapDate(_ btn: UIButton) {
        // TODO: 做什么事情？
    }
}

// 日期相关计算方法
extension ChartsContentsVC {
    
    /// 此方法返回可以向前选择的日期限制
    /// 限制在2021年以来的日期
    func pastDateLimit(with granularity: DateGranularity) -> Date {
        return Calendar.default.date(from: DateComponents(year: 2021, month: 1, day: 1))!
    }
    
    /// 此方法返回可以向后选择的日期限制
    func futureDateLimit(with granularity: DateGranularity) -> Date {
        switch granularity {
        case .day:
            // 限制到今天，明天以及以后的被限制
            return Date().nextDate(with: .day).dayStart
        case .week:
            // 限制到下一周的开始时间
            return Date().nextDate(with: .week).startOfWeek
        case .month:
            // 限制到下一个月的第一天
            return Date().nextDate(with: .month).startDayOfMonth
        case .year:
            // 限制到下一年的第一天
            return Date().nextDate(with: .year).startDayOfYear
        }
    }
    
    /// 用给定的粒度获取上一个时间点。在限制范围外返回nil
    func previousDate(of date: Date, granularity: DateGranularity) -> Date? {
        let newDate = date.previousDate(with: granularity)
        let limit = pastDateLimit(with: granularity)
        if newDate.isEarly(than: limit, granularity: granularity) {
            return nil
        }
        return newDate
    }
    
    /// 用给定的粒度获取下一个时间点。在限制范围外返回nil
    func nextDate(of date: Date, granularity: DateGranularity) -> Date? {
        let newDate = date.nextDate(with: granularity)
        let limit = futureDateLimit(with: granularity)
        if newDate.isEarly(than: limit, granularity: granularity) {
            return newDate
        }
        return nil
    }
    
    /// 此方法根据日期和粒度返回显示在中间的时间内容
    func dateTitle(of date: Date, granularity: DateGranularity) -> String {
        switch granularity {
        case .day:
            let ymd = date.yearMonthDay
            return "\(ymd.0)年\(ymd.1)月\(ymd.2)日"
        case .week:
            let curDate = Date()
            if (curDate.weekOfYear == date.weekOfYear) {
                return "本周"
            } else {
                return "\(date.yearForWeekOfYear)年第\(date.weekOfYear)周"
            }
        case .month:
            return "\(date.year)年\(date.month)月"
        case .year:
            return "\(date.year)年"
        }
    }
}

extension ChartsContentsVC: AxisValueFormatter {
    /// 此方法返回图表的 x轴和 y轴的标签
    func stringForValue(_ value: Double, axis: AxisBase?) -> String {
        if axis === chartView?.leftAxis {
            return "\(Int(value))"
        }
    
        if axis === chartView?.xAxis {
            switch granularitie {
            case .day:
                var v = Int(value)
                if v < 0 {
                    v = v % 24 + 24
                }
                if v >= 24 {
                    v = v % 24
                }
                return "\(v, minLength: 2):00"
            case .week:
                let weekday = Int(value) == 8 ? Calendar.Weekday.sunday : Calendar.Weekday(rawValue: Int(value))
                return weekday?.fullSymbolCN ?? ""
            case .month: return "\(date.month, minLength: 2)/\(Int(value), minLength: 2)"
            case .year: return "\(Int(value))月"
            case .none: break
            }
        }
        return ""
    }
}
